/*!
* @file Rotation.h
* @brief 三维旋转
* @date 2024/9/11
*/
#pragma once

#include "Vector3D.h"

namespace data
{
class Matrix4d;

/*!
* @brief 三维旋转
*/
class DATA_API Rotation
{
public:
    using traits_type = std::numeric_limits<double>;

    /// 成员
    double x, y, z, w;

    /*!
    * @brief 旋转构造
    */
    Rotation() : x(0.0), y(0.0), z(0.0), w(1.0) {}
    Rotation(const Rotation& rot) = default;
    Rotation(Rotation && rot) = default;
    explicit Rotation(const double q[4]) : x(q[0]), y(q[1]), z(q[2]), w(q[3]) {}
    explicit Rotation(double fx, double fy, double fz, double fw) : x(fx), y(fy), z(fz), w(fw) {}
    explicit Rotation(const Vector3d& axis, double angle);
    explicit Rotation(const Vector3d& from, const Vector3d& to);
    ~Rotation() = default;

    void Get(double& fx, double& fy, double& fz, double& fw) const;
    void Set(double fx, double fy, double fz, double fw);
    void Set(const double q[4]);
    void Get(Vector3d& axis, double& angle) const;
    void Set(const Vector3d& axis, double angle);
    void Set(const Vector3d& from, const Vector3d& to);

    /*!
    * @brief 旋转运算
    */
    Rotation& operator=(const Rotation&) = default;
    Rotation& operator=(Rotation&&) = default;
    Rotation& operator*=(const Rotation& q);
    Rotation operator*(const Rotation& q) const;
    bool operator==(const Rotation& q) const;
    bool operator!=(const Rotation& q) const;
    double& operator[](unsigned short index);
    const double& operator[](unsigned short index) const;

    Rotation& MultRight(const Rotation& q);
    Rotation& MultLeft(const Rotation& q);
    Vector3d MultVec(const Vector3d& src) const;
    void ScaleAngle(double factor);
    Rotation& Invert();
    Rotation Inverse() const;

    void MakeIdentity();
    bool IsIdentity() const;
    bool IsEqual(const Rotation& q, double tol) const;
    bool IsIdentity(double tol) const;

    Matrix4d ToMatrix() const;

    /*!
    * @brief 文本信息输出
    */
    friend std::ostream& operator<< (std::ostream& stream, const Rotation& r)
    {
        return stream << "{ x: " << r.x << ", y: " << r.y << ", z: " << r.z << ", w: " << r.w << " }";
    }
};

inline Vector3d operator*(const Vector3d& vec, const Rotation& q)
{
    return q.MultVec(vec);
}

inline Vector3d& operator*=(Vector3d& vec, const Rotation& q)
{
    vec = q.MultVec(vec);
    return vec;
}
}  // namespace data
